﻿using System;
using Ewk.Math.ComputationTypes;

namespace Ewk.Math.Algebra.Algorithms.Matrices.Determinant
{
    internal class DeterminantManipulator<T> : IMatrixManipulations<T, Computable<T>>
        where T : struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
    {
        private readonly Matrix<T> _matrix;
        private Computable<T> _value;

        public DeterminantManipulator(Matrix<T> matrix, Computable<T> value)
        {
            _matrix = matrix;
            _value = value;
        }

        #region Implementation of IMatrixManipulations

        public Computable<T> SwapRows(int index1, int index2)
        {
            return Swap(index1, index2);
        }

        public Computable<T> SwapColumns(int index1, int index2)
        {
            return Swap(index1, index2);
        }

        public Computable<T> Add(Computable<T> value)
        {
            _value = null;
            return _value;
        }

        public Computable<T> Add(Matrix<T> matrix)
        {
            _value = null;
            return _value;
        }

        public Computable<T> Subtract(Computable<T> value)
        {
            _value = null;
            return _value;
        }

        public Computable<T> Subtract(Matrix<T> matrix)
        {
            _value = null;
            return _value;
        }

        public Computable<T> Multiply(Computable<T> value)
        {
            if (value == null) throw new ArgumentNullException("value");
            if (_value == null) return null;

            var i = _matrix.ColumnCount;
            var exponent = Computable<T>.Factory.One;

            while (--i>0)
            {
                exponent.Add(Computable<T>.Factory.One);
            }

            _value *= Computable<T>.Pow(value, exponent);
            return _value;
        }

        public Computable<T> Multiply(Matrix<T> matrix)
        {
            if (matrix == null) throw new ArgumentNullException("matrix");
            if (_value == null) return null;

            _value *= matrix.Determinant;
            return _value;
        }

        public Computable<T> Divide(Computable<T> value)
        {
            return Multiply(Computable<T>.Factory.One/value);
        }

        public Computable<T> Transpose()
        {
            return _value;
        }

        public Computable<T> Invert()
        {
            if (_value == null) return null;

            _value = Computable<T>.Factory.One / _value;
            return _value;
        }

        public Computable<T> Adjugate()
        {
            throw new NotImplementedException();
        }

        #endregion

        private Computable<T> Swap(int index1, int index2)
        {
            if (_value == null) return null;

            if (index1 != index2)
            {
                _value = _value * (Computable<T>.Factory.Zero - Computable<T>.Factory.One);
            }

            return _value;
        }
    }
}